# 导入必要的库
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
import pandas as pd

# 设置中文显示（确保中文正常显示）
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用 SimHei 字体支持中文
plt.rcParams['axes.unicode_minus'] = False    # 正常显示负号

# 1. 加载鸢尾花数据集
iris = load_iris()
X = iris.data  # 特征数据 (花萼长度、花萼宽度、花瓣长度、花瓣宽度)
y = iris.target  # 目标变量 (0: setosa, 1: versicolor, 2: virginica)

# 2. 转换为二分类问题
# 只选择 setosa (0) 和 versicolor (1)，忽略 virginica (2)
mask = (y == 0) | (y == 1)
X = X[mask]
y = y[mask]

# 3. 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 4. 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 5. 创建并训练逻辑回归模型
model = LogisticRegression(random_state=42, max_iter=200)
model.fit(X_train_scaled, y_train)

# 6. 预测
y_pred = model.predict(X_test_scaled)

# 7. 模型评估
# 计算准确率
accuracy = accuracy_score(y_test, y_pred)
# 计算混淆矩阵
conf_matrix = confusion_matrix(y_test, y_pred)
# 分类报告（精确率、召回率、F1 分数等）
class_report = classification_report(y_test, y_pred, target_names=['setosa', 'versicolor'])

# 打印结果
print("逻辑回归模型准确率:", accuracy)
print("\n混淆矩阵:\n", conf_matrix)
print("\n分类报告:\n", class_report)

# 添加 ROC 曲线和 AUC
from sklearn.metrics import roc_curve, auc
y_prob = model.predict_proba(X_test_scaled)[:, 1]
fpr, tpr, _ = roc_curve(y_test, y_prob)
roc_auc = auc(fpr, tpr)
plt.figure()
plt.plot(fpr, tpr, label=f'ROC 曲线 (AUC = {roc_auc:.2f})')
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('假正率 (FPR)')
plt.ylabel('真正率 (TPR)')
plt.title('ROC 曲线')
plt.legend()
plt.show()

# 添加对数损失
from sklearn.metrics import log_loss
logloss = log_loss(y_test, y_prob)
print("对数损失:", logloss)

# 8. 可视化混淆矩阵（中文注释）
plt.figure(figsize=(6, 5))
sns.heatmap(conf_matrix, annot=True, fmt='d', cmap='Blues', cbar=False,
            xticklabels=['setosa', 'versicolor'], yticklabels=['setosa', 'versicolor'])
plt.title('逻辑回归混淆矩阵')  # 中文标题
plt.xlabel('预测类别')  # 中文 x 轴标签
plt.ylabel('真实类别')  # 中文 y 轴标签
plt.show()

# 9. 可视化决策边界（使用前两个特征：花萼长度和花萼宽度，中文注释）
def plot_decision_boundary(X, y, model, scaler):
    # 提取前两个特征
    X_2d = X[:, [0, 1]]  # 花萼长度和花萼宽度
    x_min, x_max = X_2d[:, 0].min() - 1, X_2d[:, 0].max() + 1
    y_min, y_max = X_2d[:, 1].min() - 1, X_2d[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.1),
                         np.arange(y_min, y_max, 0.1))

    # 使用训练数据的平均值填充其他特征（花瓣长度和花瓣宽度）
    other_features = np.mean(X_train[:, 2:], axis=0)  # 使用训练数据的均值
    X_grid = np.c_[xx.ravel(), yy.ravel(),
                   np.full_like(xx.ravel(), other_features[0]),  # 花瓣长度均值
                   np.full_like(xx.ravel(), other_features[1])]  # 花瓣宽度均值
    Z = model.predict(scaler.transform(X_grid))
    Z = Z.reshape(xx.shape)

    # 绘制决策边界和散点图（中文注释）
    plt.figure(figsize=(8, 6))
    # 使用 cmap='coolwarm' 确保底色区分
    plt.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm', levels=[-1, 0, 1])  # levels 调整为 [-1, 0, 1]，确保二分类颜色区分
    plt.scatter(X_2d[y == 0, 0], X_2d[y == 0, 1], label='山鸢尾 (setosa)', color='blue', edgecolor='k')
    plt.scatter(X_2d[y == 1, 0], X_2d[y == 1, 1], label='变色鸢尾 (versicolor)', color='red', edgecolor='k')
    plt.xlabel('花萼长度 (cm)')  # 中文 x 轴标签
    plt.ylabel('花萼宽度 (cm)')  # 中文 y 轴标签
    plt.title('逻辑回归决策边界')  # 中文标题
    plt.legend()  # 图例已使用中文
    plt.show()

# 调用函数绘制决策边界
plot_decision_boundary(X, y, model, scaler)

# 10. 打印模型系数
print("\n逻辑回归模型系数:\n", pd.Series(model.coef_[0], index=iris.feature_names))
print("截距:", model.intercept_)